#include "modules/buffer_mgr.h"

static inline string dbname(string &tblname)
{
	return "/dbs/" + tblname + ".db";
}

void BufferManager::create_table(string &tblname)
{
	//create file
	int fd = open(dbname(tblname).c_str(), O_CREAT | O_EXCL);
	if(fd < 0)
	{
		throw Exception(buffermgr_createtbl_failed);
	}
	close(fd);
}

void BufferManager::drop_table(string &tblname)
{
	//TBD: Check if there are any buffers i memory and discard them
	if(unlink(dbname(tblname).c_str()) < 0)
	{
		throw Exception(buffermgr_droptbl_failed);
	}
}

UINT32 BufferManager::create_block(string &tblname)
{
	do
	{
		int fd = open(dbname(tblname).c_str(), O_RDWR);
		if(fd <0)
			break;
		
		int size = lseek(fd, 0, SEEK_END);
		if(size < 0) 
			break;
		
		BYTE *temp = new BYTE[block_size];
		memset(temp,0,block_size);
		
		if(::write(fd, temp, block_size) < 0)
		{
			delete [] temp;
			break;
		}
		
		delete [] temp;		
		close(fd);
		return size/block_size;
	} while(0);
	
	throw Exception(buffermgr_createblk_failed);
}

void BufferManager::delete_block(string &tblname)
{
	//Not Implemented
	throw Exception(buffermgr_unknown);
}

void BufferManager::read(string &tblname, UINT32 block, BYTE *buffer)
{
	BufferInfo info = fetch_block(tblname, block);
	memcpy(buffer, BufferPool[info.bp_index], block_size);
}

void BufferManager::write(string &tblname, UINT32 block, BYTE *buffer)
{
	BufferKey key = {tblname, block};
	BufferInfo info = fetch_block(tblname, block);
	memcpy(BufferPool[info.bp_index], buffer, block_size);
	info.isdirty = true;
	bit.insert(key,info); //Update the buffer info table
}

void BufferManager::commit()
{
	//flush all the blocks in buffer
}

BufferInfo BufferManager::fetch_block(string &tblname, UINT32 block)
{
	BufferKey key = {tblname, block};
	BufferInfo info;
	try
	{
		info =  bit.lookup(key);
		return info;
	}
	catch(...)
	{
		BufferKey victim_key = choose_victim();
		BufferInfo victim_info = bit.lookup(victim_key);
		if(victim_info.isdirty)
		{
			write_file(victim_key.tablename, victim_key.block_num, BufferPool[victim_info.bp_index]);
		}
		bit.remove(victim_key);
		
		info.bp_index = victim_info.bp_index;
		info.isdirty = false;
		read_file(tblname, block, BufferPool[info.bp_index]);
		bit.insert(key, info);
		return info;
	}
}

BufferKey BufferManager::choose_victim()
{
	BufferKey dummy_key;
	return dummy_key;
}

void BufferManager::write_file(string &tblname, UINT32 block_num, BYTE *data)
{
	do
	{
		int fd = open(dbname(tblname).c_str(), O_RDWR);
		if(fd <0)
			break;
		
		int size = lseek(fd, (block_num - 1) * block_size, SEEK_SET);
		if(size < 0) 
			break;
		
		BYTE *temp = new BYTE[block_size];
		memcpy(temp,data,block_size);
		
		if(::write(fd, temp, block_size) < 0)
		{
			delete [] temp;
			break;
		}
		
		delete [] temp;		
		close(fd);
	} while(0);
	
	throw Exception(buffermgr_createblk_failed);
}

void BufferManager::read_file(string &tblname, UINT32 block_num, BYTE *data)
{
	do
	{
		int fd = open(dbname(tblname).c_str(), O_RDWR);
		if(fd <0)
			break;
		
		int size = lseek(fd, (block_num - 1) * block_size, SEEK_SET);
		if(size < 0) 
			break;
		
		if(::read(fd, data, block_size) < 0)
		{
			break;
		}
		
		close(fd);
	} while(0);
	
	throw Exception(buffermgr_createblk_failed);
}
